42 数据结构之元组
本章定位(复习与补充训练层):本章对应正课第2章《列表、字典和元组》(章节 2)的元组部分,并与第37章《数据基本结构(元组、列表、字典)》(章节 38)互为补充训练(本章为分项版),用于复习、补缺与额外练习。建议先不看讲解,直接尝试下方平台任务,再对照解析补弱项。本章不属于必修主线。先做本章『动手与思考』第 1 题与平台任务自测,通过即可跳过本章。
42.1 引言:元组的不可变性及其在数据科学中的意义
元组(Tuple)的核心特征是不可变性(Immutability)——通过限制修改来保证数据的完整与安全,这使它特别适合存储交易参数、历史行情等一经生成就不应改变的内容。本章用三个平台任务检验你对元组创建、索引切片与整体重建的复习效果。
42.2 本章学习目标
本章为复习与补充训练,目标是检验你是否能脱离正课独立完成下列任务:
- 能独立完成平台任务1:用
type()逐一检查股票信息各变量(名称、代码、日期、交易所、总股本、流通股本、发行价、现价、涨跌幅)的数据类型 - 能独立完成平台任务2:把上述九个字段打包成元组
tup,并输出整个元组、首元素tup[0]、末元素tup[-1]与切片tup[2:6] - 能独立完成平台任务3:用
del删除整个元组变量,再把含 H 股价格与涨跌幅的十一个字段打包成新元组tup_new输出 - 能说出单元素元组必须带逗号、元组”不可变但可整体重建”、以及元组能作字典键而列表不能的原因
若上述任务无法独立完成,请回到正课第2章元组部分补弱,再回到本章练习。
42.3 元组的五大核心特性
元组之所以在Python生态系统中占据重要地位,源于其以下五个相互关联的核心特性:
1. 不可变性(Immutability)
不可变性是元组最本质的特征。这意味着一旦元组被创建,其内容不能被增加、删除或修改。这一特性看似是一种限制,实则是编程中的重要保障机制。从软件工程角度看,不可变性带来了:
- 数据一致性: 消除了因意外修改导致的数据不一致风险
- 代码可预测性: 调用函数时,传入的元组参数不会被函数内部操作改变
- 内存共享安全: 多个变量可以安全地引用同一个元组,无需担心副作用
再从计算理论的角度看,不可变数据结构具有显著优势:
- 线程安全: 多线程环境下,不可变对象无需加锁即可安全访问
- 哈希能力: 只有不可变对象才能作为字典的键或集合的元素
- 内存优化: Python解释器可以对元组进行特定优化,减少内存占用
- 语义清晰: 不可变性明确传达了”只读”的编程意图
2. 有序性(Ordered)
元组中的元素按照插入顺序存储,可以通过索引(从0开始)访问。这一点与列表相同,但与集合、字典等无序结构形成对比。在金融时间序列分析中,顺序性至关重要——数据点的先后次序往往代表了时间先后,这种信息本身就有价值。
3. 异构性(Heterogeneous)
一个元组可以包含不同类型的元素:整数、浮点数、字符串、甚至其他元组。这种灵活性使得元组特别适合表示”记录”或”结构体”。例如,一个股票的tick数据可以表示为:
tick_data = ('600519.SH', '2024-01-15 09:30:00', 1856.00, 100)
# 分别代表:股票代码、时间戳、价格、成交量4. 轻量性(Lightweight)
由于元组的不可变性,Python解释器可以对其进行特定优化。在内存占用方面,元组通常比列表更节省空间。在C语言实现中,元组对象的结构比列表简单,不需要维护动态扩容的额外开销。对于需要创建大量小对象的场景,使用元组可以显著降低内存压力。
5. 可迭代性(Iterable)
元组是可迭代对象,可以用于for循环、列表推导式等上下文。这使得元组可以无缝集成到Python的数据处理流程中,与其他数据结构配合使用。
42.4 元组的创建方法
Python提供了多种创建元组的方式,每种方式都有其适用场景和注意事项。理解这些方法的细微差别有助于编写更清晰、更高效的代码。
方法1:使用圆括号创建
最常见的方法是使用圆括号将元素括起来,元素之间用逗号分隔。
平台任务1(平台原始代码)
以下代码与教学平台任务要求完全一致:
任务要求:给定中国通号A股的九个字段变量(名称、代码、上市日期、交易所、总股本、流通股本、发行价、现价、涨跌幅),逐一用 type() 输出各变量的数据类型。请将代码原样输入教学平台(注释除外),判定以平台为准。
# ⚠️ 平台原始代码 - 请原样输入至教学平台(注释除外),平台才会判定答案正确
name = "中国通号" # 设置名称为"中国通号"
print('name:',type(name)) # 输出name
code = "688009" # 设置代码为"688009"
print('code:',type(code)) # 输出code
IPO_date = "2019年7月22日" # 设置日期为"2019年7月22日"
print('IPO_data:',type(IPO_date)) # 输出IPO_data
exchange = "上海证券交易所" #以字符串输入中国通号A股的上市交易所
print('exchange',type(exchange)) #使用type函数输出数据类型
capital = 10589819000 # 总股本:10589819000股
print('capital',type(capital)) # 输出capital
shares = 8621018000 # 流通股本:8621018000股
print('share:',type(shares)) # 输出share
price_IPO = 5.85 #以浮点型输入中国通号A股发行价
print('price_IPO:',type(price_IPO)) #使用type函数输出数据类型并使用print函数打印
price_Jul22 = 12.27 # 设置当前价格为12.27
print('price_Jul22:',type(price_Jul22)) # 输出price_Jul22
change_Jul22 = 1.09744 # 涨幅:109.744%
print('change_Jul22:',type(change_Jul22)) # 输出change_Jul22预期输出(本机实际运行结果,具体以平台运行结果为准):
name: <class 'str'>
code: <class 'str'>
IPO_data: <class 'str'>
exchange <class 'str'>
capital <class 'int'>
share: <class 'int'>
price_IPO: <class 'float'>
price_Jul22: <class 'float'>
change_Jul22: <class 'float'>
平台任务2(平台原始代码)
以下代码与教学平台任务要求完全一致:
任务要求:把上述九个字段打包成元组 tup,输出整个元组,再分别访问并输出首元素 tup[0]、末元素 tup[-1] 与切片 tup[2:6]。请将代码原样输入教学平台(注释除外),判定以平台为准。
# ⚠️ 平台原始代码 - 请原样输入至教学平台(注释除外),平台才会判定答案正确
name = "中国通号" # 设置名称为"中国通号"
code = "688009" # 设置代码为"688009"
IPO_date = "2019年7月22日" # 设置日期为"2019年7月22日"
exchange = "上海证券交易所" # 设置交易所为"上海证券交易所"
capital = 10589819000 # 总股本:10589819000股
shares = 8621018000 # 流通股本:8621018000股
price_IPO = 5.85 # 设置当前价格为5.85
price_Jul22 = 12.27 # 设置当前价格为12.27
change_Jul22 = 1.09744 # 涨幅:109.744%
tup =(name,code,IPO_date,exchange,capital,shares,price_IPO,price_Jul22,change_Jul22) #创建一个元组,包括以上数据
print(tup) #打印输出这个元组
print(tup[0]) #访问该元组的首个元素
print(tup[-1]) #访问该元组的末尾元素
print(tup[2:6])#访问该元组的第3个至第6个元素预期输出(本机实际运行结果,具体以平台运行结果为准):
('中国通号', '688009', '2019年7月22日', '上海证券交易所', 10589819000, 8621018000, 5.85, 12.27, 1.09744)
中国通号
1.09744
('2019年7月22日', '上海证券交易所', 10589819000, 8621018000)
平台任务3(平台原始代码)
以下代码与教学平台任务要求完全一致:
任务要求:用 del 删除任务2中创建的元组 tup,再新增H股收盘价与H股涨跌幅两个字段,把十一个字段打包成新元组 tup_new 并输出。请将代码原样输入教学平台(注释除外),判定以平台为准。
# ⚠️ 平台原始代码 - 请原样输入至教学平台(注释除外),平台才会判定答案正确
name = "中国通号" # 设置名称为"中国通号"
code = "688009" # 设置代码为"688009"
IPO_date = "2019年7月22日" # 设置日期为"2019年7月22日"
exchange = "上海证券交易所" # 设置交易所为"上海证券交易所"
capital = 10589819000 # 总股本:10589819000股
shares = 8621018000 # 流通股本:8621018000股
price_IPO = 5.85 # 设置当前价格为5.85
price_Jul22 = 12.27 # 设置当前价格为12.27
change_Jul22 = 1.09744 # 涨幅:109.744%
tup = (name,code,IPO_date,exchange,capital,shares,price_IPO,price_Jul22,change_Jul22) # 定义元组tup
price_Jul22_H = 5.43 #以浮点型输入中国通号H股7月22日收盘价
change_Jul22_H = -0.1171 #以浮点数输入中国通号H股7月22日涨跌幅
del tup #删除任务2中创建的元组
tup_new = (name,code,IPO_date,exchange,capital,shares,price_IPO,price_Jul22,change_Jul22,price_Jul22_H,change_Jul22_H) #创建包含H股股价和涨跌幅的新元组
print(tup_new) #打印这个新元组预期输出(本机实际运行结果,具体以平台运行结果为准):
('中国通号', '688009', '2019年7月22日', '上海证券交易所', 10589819000, 8621018000, 5.85, 12.27, 1.09744, 5.43, -0.1171)
# 方式1: 使用圆括号创建元组
# 圆括号是可选的,但加上可以提高代码可读性
tup1 = ('physics', 'chemistry', 1997, 2000) # 包含字符串和整数的混合元组
tup2 = (1, 2, 3, 4, 5) # 纯整数元组
tup3 = "a", "b", "c", "d" # 不加括号也可以,Python会自动识别为元组
# 创建空元组
# 空元组常用于初始化变量或作为函数默认参数
empty_tup = ()
# 创建单元素元组(注意逗号!)
# 这是一个常见的陷阱:单个元素后面必须有逗号,否则会被识别为带括号的表达式
single_tup = (50,) # 正确:有逗号,是元组
not_a_tuple = (50) # 错误:没有逗号,这只是整数50
# 打印结果以验证类型
print('元组1:', tup1)
print('元组2:', tup2)
print('元组3:', tup3) # 证明不加括号也能创建元组
print('空元组:', empty_tup)
print('单元素元组:', single_tup)方法2:使用tuple()构造函数
tuple()构造函数可以将其他可迭代对象转换为元组:
# 从列表创建元组
list_data = [1, 2, 3, 4]
tup_from_list = tuple(list_data)
# 从字符串创建元组(每个字符成为一个元素)
tup_from_string = tuple('Hello')
# 从range对象创建元组
tup_from_range = tuple(range(5))
print('从列表:', tup_from_list)
print('从字符串:', tup_from_string)
print('从range:', tup_from_range)方法3:元组推导式(Tuple Comprehension)
虽然Python没有专门的元组推导式语法,但可以使用生成器表达式配合tuple()函数:
# 使用生成器表达式创建元组
# 生成器表达式用圆括号表示,然后传给tuple()函数
squares = tuple(x**2 for x in range(6))
# 这等价于先创建列表再转换,但更节省内存
# 因为生成器是惰性求值的,不需要创建中间列表
print('平方数元组:', squares)创建方法的选择指南
| 场景 | 推荐方法 | 理由 |
|---|---|---|
| 已知元素,数量少 | tup = (1, 2) |
简洁直观 |
| 从其他序列转换 | tuple(seq) |
清晰表达意图 |
| 需要计算生成元素 | tuple(x for x in ...) |
内存高效 |
| 需要空元组 | empty = () |
最简洁 |
42.5 元组的索引与切片操作
作为有序数据结构,元组支持强大的索引和切片机制,这使得访问元组中的元素或子元组变得非常灵活和高效。
基本索引
元组的索引从0开始,支持正向索引(从左到右)和负向索引(从右到左):
# 创建示例元组
tup1 = ('physics', 'chemistry', 1997, 2000) # 混合类型元组
tup2 = (1, 2, 3, 4, 5, 6, 7) # 纯数字元组
# 正向索引:从0开始,从左向右数
print('tup1[0]:', tup1[0]) # 第一个元素:'physics'
print('tup1[1]:', tup1[1]) # 第二个元素:'chemistry'
# 负向索引:从-1开始,从右向左数
print('tup2[-1]:', tup2[-1]) # 最后一个元素:7
print('tup2[-2]:', tup2[-2]) # 倒数第二个元素:6
# 切片操作:获取子元组
# 语法:tup[start:end:step]
# start包含,end不包含(左闭右开区间)
print('tup2[1:5]:', tup2[1:5]) # 索引1到4的元素:(2, 3, 4, 5)
print('tup2[::2]:', tup2[::2]) # 每隔一个取一个:(1, 3, 5, 7)
print('tup2[::-1]:', tup2[::-1]) # 反转元组:(7, 6, 5, 4, 3, 2, 1)切片操作的详细说明
切片是Python中最强大和最常用的特性之一,理解其工作原理对于高效数据处理至关重要:
- 基本切片
[start:end]:从start索引到end-1索引 - 带步长切片
[start:end:step]:按step步长取元素 - 省略参数:
[:end]从头开始,[start:]到结尾,[::]全部 - 负步长:表示反向取元素,常用于反转序列
索引越界处理
与列表不同,元组的不可变性意味着其长度是固定的。访问不存在的索引会引发IndexError:
tup = (1, 2, 3)
# 下面的代码会抛出IndexError
try:
print(tup[10]) # 索引10不存在
except IndexError as e:
print(f'错误:索引超出范围 - {e}')为了避免索引越界,可以使用条件检查或异常处理,或者先检查元组长度。
42.6 元组的不可变性:原理与实践
元组的不可变性是其最核心的特征,理解这一特性的含义、原因和影响对于正确使用元组至关重要。
不可变性的含义
“不可变”并不意味着我们完全不能改变与元组相关的变量,而是指:
- 不能修改元组中的元素
- 不能删除元组中的元素
- 不能向元组中添加新元素
- 但可以重新给变量赋值一个新的元组
# 创建一个元组
tup = (12, 34.56)
# 尝试修改元组的第一个元素
try:
tup[0] = 20 # 这会抛出TypeError
except TypeError as e:
print(f'错误类型: {type(e).__name__}')
print(f'错误信息: {e}')
print('结论:元组元素一旦创建就不能修改')
# 虽然不能修改,但可以连接两个元组创建新元组
# 注意:这不是修改原元组,而是创建了一个全新的元组
tup3 = tup + ('abc', 'xyz')
print('原元组:', tup) # 原元组保持不变
print('新元组:', tup3) # 这是一个新创建的元组
# 也可以通过重复创建新元组
tup4 = tup * 2 # 元组重复
print('重复元组:', tup4)为什么需要不可变性?
不可变性在软件工程和数据科学中都有重要意义:
数据安全性:在金融应用中,某些数据(如历史交易记录)一旦生成就不应被修改。元组的不可变性提供了语言层面的保证。
多线程环境:不可变对象是线程安全的,多个线程可以同时访问而无需加锁,这在并行计算和高频交易系统中非常重要。
哈希要求:只有不可变对象才能被哈希,因此只有不可变对象才能作为字典的键或集合的元素。这对于构建查找表、缓存等数据结构至关重要。
函数式编程:不可变性是函数式编程的核心原则之一,它使得程序更易于推理、测试和并行化。
内存优化:由于元组不可变,Python可以对相同内容的元组进行 intern 优化,共享内存。
可变对象陷阱
需要注意的是,如果元组包含可变对象(如列表),那么这些可变对象的内容是可以修改的,例如对 t = (1, [2, 3]) 执行 t[1].append(4) 是合法的——不可变的是元组的结构,不是内层对象本身。这种情况下,建议要么确保元组只包含不可变对象,要么使用文档明确说明这种行为的限制。
42.7 元组在金融数据分析中的应用
元组在金融领域有广泛的应用场景,其不可变性和简洁性使其成为处理金融数据的理想选择。具体而言,不可变性在数据科学与金融分析中的特殊价值体现在:
- 交易参数保护: 交易策略的核心参数(如止损线、杠杆比例)一旦设定就不应被意外修改
- 数据完整性保证: 历史价格数据、公司基本信息等静态数据适合用元组存储
- 多值返回: 金融函数经常需要返回多个相关值(如均值和标准差),元组是最自然的载体
- 字典键的需求: 按日期-股票代码这样的组合查找数据时,元组键是必需的
应用1:交易参数配置
在量化交易系统中,策略参数一旦设定就不应被意外修改,元组提供了天然的保障:
# 定义交易策略的核心参数
# 使用元组确保这些参数不会被意外修改
trading_params = (
100000, # 初始资金(元)
0.05, # 预期年收益率
30, # 投资期限(年)
0.6 # 股票配置比例
)
# 元组解包:将元组的元素分别赋值给变量
# 这种写法比分别访问元组元素更清晰
initial_capital, rate, years, stock_ratio = trading_params
# 格式化输出参数
# 使用f-string进行格式化,提高可读性
print(f'初始资金: {initial_capital:,.0f}元') # 添加千位分隔符
print(f'年利率: {rate:.1%}') # 百分比格式
print(f'投资期限: {years}年')
print(f'股票配置: {stock_ratio:.0%}')
# 计算期末资产
final_amount = initial_capital * (1 + rate) ** years
print(f'期末资产(复利): {final_amount:,.2f}元')应用2:金融数据的键值对
当需要用复合键查找数据时,元组是不可替代的选择。例如,按照(股票代码, 日期)查找价格:
# 创建价格查找表
# 键是(股票代码, 日期)的元组,值是收盘价
price_lookup = {
('600519.SH', '2024-01-15'): 1856.00,
('600519.SH', '2024-01-16'): 1862.50,
('000858.SZ', '2024-01-15'): 158.20,
('000858.SZ', '2024-01-16'): 159.80
}
# 查询特定股票在特定日期的价格
stock = '600519.SH'
date = '2024-01-15'
price = price_lookup.get((stock, date))
if price:
print(f'{stock} 在 {date} 的收盘价是 {price:.2f}元')
else:
print('未找到匹配的价格数据')
# 只有不可变对象(如元组)才能作为字典键
# 下面的代码会报错,因为列表是可变的:
# error_dict = {['600519.SH', '2024-01-15']: 1856.00} # TypeError应用3:函数返回多个值
金融函数经常需要返回多个相关的值,元组是最自然的方式:
def calculate_return_metrics(prices):
'''
计算价格序列的多个收益率指标
参数:
prices: 价格列表
返回:
(总收益率, 年化收益率, 波动率)的元组
'''
import numpy as np
# 计算简单收益率
returns = np.diff(prices) / prices[:-1]
# 总收益率
total_return = (prices[-1] - prices[0]) / prices[0]
# 年化收益率(假设252个交易日)
annual_return = (1 + total_return) ** (252 / len(prices)) - 1
# 年化波动率
annual_volatility = returns.std() * np.sqrt(252)
return total_return, annual_return, annual_volatility
# 使用示例
stock_prices = [100, 102, 101, 105, 108, 107, 110]
total_ret, ann_ret, ann_vol = calculate_return_metrics(stock_prices)
print(f'总收益率: {total_ret:.2%}')
print(f'年化收益率: {ann_ret:.2%}')
print(f'年化波动率: {ann_vol:.2%}')应用4:时间序列数据的不可变记录
在处理金融时间序列时,元组可以表示固定的记录:
# 定义一个时间戳数据点
tick = ('600519.SH', '2024-01-15 09:30:00', 1856.00, 100)
# 格式:(股票代码, 时间戳, 价格, 成交量)
# 元组解包提取字段
symbol, timestamp, price, volume = tick
print(f'股票代码: {symbol}')
print(f'时间戳: {timestamp}')
print(f'价格: {price:.2f}元')
print(f'成交量: {volume}手')
# 将多个tick数据组成列表
ticks = [
('600519.SH', '09:30:00', 1856.00, 100),
('600519.SH', '09:31:00', 1858.50, 150),
('600519.SH', '09:32:00', 1855.00, 80),
]
# 分析第一分钟的价格变化
price_change = ticks[1][2] - ticks[0][2]
print(f'第一分钟价格变化: {price_change:+.2f}元')42.8 元组与列表的选择策略
理解何时使用元组、何时使用列表是编写Python代码的重要技能。以下是决策指南:
使用元组的场景:
- 数据需要保护:当数据是配置参数、常量,或需要防止意外修改时
- 需要字典键:当需要用复合数据作为字典的键时
- 性能敏感:当需要创建大量小对象,且不需要修改时
- 函数返回值:当函数需要返回多个值时
- 数据记录:当表示固定的记录结构(如数据库查询结果)时
使用列表的场景:
- 数据会变化:当需要频繁增删元素时
- 数据积累:当逐步构建数据序列时
- 内存缓冲:当需要临时存储中间结果时
性能对比
在内存使用和访问速度方面,元组通常优于列表:
import sys
import timeit
# 创建相同内容的元组和列表
tup = tuple(range(1000))
lst = list(range(1000))
# 内存占用比较
print(f'元组内存占用: {sys.getsizeof(tup)} 字节')
print(f'列表内存占用: {sys.getsizeof(lst)} 字节')
print(f'元组更节省: {sys.getsizeof(lst) - sys.getsizeof(tup)} 字节')
# 访问速度比较
tup_time = timeit.timeit('x = tup[500]', globals=globals(), number=1000000)
lst_time = timeit.timeit('x = lst[500]', globals=globals(), number=1000000)
print(f'元组访问时间: {tup_time:.4f}秒')
print(f'列表访问时间: {lst_time:.4f}秒')
print(f'元组速度提升: {(lst_time/tup_time - 1)*100:.1f}%')42.9 命名元组:增强版元组
Python的collections模块提供了namedtuple,它创建了一个带字段名的元组子类,结合了元组的不可变性和对象的属性访问便利性:
from collections import namedtuple
# 定义一个命名元组类型
# 相当于创建了一个简单的类
Stock = namedtuple('Stock', ['symbol', 'price', 'volume'])
# 创建Stock实例
stock1 = Stock('600519.SH', 1856.00, 1000)
stock2 = Stock(symbol='000858.SZ', price=158.20, volume=5000)
# 可以像元组一样索引访问
print(f'股票代码(索引): {stock1[0]}')
# 也可以像对象一样属性访问
print(f'股票代码(属性): {stock1.symbol}')
print(f'价格: {stock1.price}')
print(f'成交量: {stock1.volume}')
# 命名元组仍然是元组,是不可变的
print(f'是否为元组: {isinstance(stock1, tuple)}')
# 命名元组有友好的字符串表示
print(f'股票信息: {stock1}')命名元组特别适合表示简单的数据记录,比普通元组更清晰,比定义完整的类更轻量。
42.10 延伸阅读(选读)
历史背景与理论发展
元组的概念起源于数学中的有序对(Ordered Pair)概念,由波兰-美国数学家阿尔弗雷德·塔尔斯基(Alfred Tarski)等人在20世纪初形式化。在编程语言领域,元组最早出现在Lisp语言中(1958年),随后被各种语言采纳。Python的元组设计深受函数式编程影响,其不可变性直接对应于数学中元组的定义——一旦创建,其元素和顺序就固定不变。
42.11 本章小结
要点:
- 元组五大特性:不可变、有序、异构、轻量、可迭代;混合类型的”记录”用元组表达最自然
- 创建方式:圆括号(可省略)、
tuple(可迭代对象)、tuple(生成器表达式);(50,)才是单元素元组 - 索引与切片和列表同构:
tup[-1]取末元素、tup[2:6]取索引2~5、tup[::-1]反转;越界抛IndexError del tup删除的是整个变量绑定而非元素;元组不支持元素级增删改,但可用+、*生成新元组- 元组解包可一次取出多个字段;命名元组
namedtuple兼具索引访问与属性访问;(代码, 日期) 这类元组可作字典键
使用建议:
- 不会改变的配置数据、函数的多值返回、需要作字典键的复合数据,优先用元组而非列表
- 表示带字段名的固定记录时,用命名元组提高代码可读性
- 用文档说明元组参数的结构和含义
易错点:
(50)只是被括号包围的整数,(50,)才是元组——单元素后的逗号不可省tup[0] = 20与del tup[0]都会抛TypeError;“修改”元组的正确方式是重建新元组再重新绑定变量- 拼接
tup + ('abc',)右侧必须是元组,写成字符串会报错 - 嵌套可变对象:
t = (1, [2, 3])中执行t[1].append(4)是合法的——不可变的是元组的结构,不是内层对象本身
42.12 动手与思考
以下练习每题附参考答案(默认折叠)。请先独立完成并写下你的判断,再点开对照,最后上机验证。
概念自测(不看正课):元组的五个核心特性是什么?为什么元组能作字典的键而列表不能?
del tup与del tup[0]有什么区别?参考答案(点开前请先独立完成)
解题思路:五个核心特性——不可变、有序、异构(可混合类型)、轻量(比列表省内存)、可迭代。元组能作字典的键是因为它不可变、可哈希,哈希值终生稳定;列表可变、不可哈希,内容一变哈希定位就失效,Python 直接禁止其作键。
del tup删除的是整个变量绑定(此后访问tup报NameError),合法;del tup[0]试图删除元组内的元素,抛TypeError——元组不支持元素级增删改。回扣主线章:元组与列表、字典的系统对比见第 章节 2 章。
输出预测:不运行代码,先写出下面代码的输出结果,再上机检验你的判断。
t = ('600519', '贵州茅台', 1850.0, 100) print(t[-1]) print(t[1:3]) print(len(t)) single = (50,) print(type(single).__name__) new_t = t[:2] + ('SH',) print(new_t)参考答案(先写下你的预测再点开)
解题思路:
t[-1]取末元素,输出100;t[1:3]含头不含尾取索引 1、2,输出('贵州茅台', 1850.0);len(t)输出4;(50,)是带逗号的单元素元组,type(single).__name__输出tuple;t[:2] + ('SH',)用拼接生成新元组(不修改原元组),输出('600519', '贵州茅台', 'SH')——注意'SH',的逗号不可省。# 验证脚本:原样运行题面代码 t = ('600519', '贵州茅台', 1850.0, 100) # 股票信息元组 print(t[-1]) # 负索引取末元素 print(t[1:3]) # 切片取索引1、2(含头不含尾) print(len(t)) # 输出元素个数 single = (50,) # 单元素元组,逗号不可省 print(type(single).__name__) # 输出类型名,避免打印带class前缀 new_t = t[:2] + ('SH',) # 拼接生成新元组 print(new_t) # 输出新元组预期输出(本机 peter 环境实际运行结果,具体以平台运行结果为准):
100 ('贵州茅台', 1850.0) 4 tuple ('600519', '贵州茅台', 'SH')回扣主线章:元组索引、切片与单元素写法的讲解见第 章节 2 章。
动手验证:对
t = (12, 34.56),先尝试执行t[0] = 20(捕获错误并说出错误类型),再执行t = t + ('abc', 'xyz')并输出t,解释第二次”修改”为什么能成功。参考答案(点开前请先独立完成)
解题思路:
t[0] = 20抛TypeError: 'tuple' object does not support item assignment——元组不可就地修改。t = t + ('abc', 'xyz')能“成功”是因为它并非修改:+先创建全新元组(12, 34.56, 'abc', 'xyz'),再把变量名t重新绑定到这个新对象;旧元组本身从未被改动,变的只是变量指向。# 验证脚本:先看元素级赋值报错,再看拼接重建 t = (12, 34.56) # 定义元组 try: t[0] = 20 # 尝试就地修改元素(预期报错) except TypeError as error_msg: print('错误类型:', type(error_msg).__name__, '|', error_msg) # 捕获并输出错误 t = t + ('abc', 'xyz') # 拼接生成新元组并重新绑定变量名t print(t) # 输出新元组预期输出(本机 peter 环境实际运行结果,具体以平台运行结果为准):
错误类型: TypeError | 'tuple' object does not support item assignment (12, 34.56, 'abc', 'xyz')回扣主线章:不可变性与“重建再绑定”的讲解见第 章节 2 章。
变式任务(平台任务2、3同型改造):仿照平台任务2,为一只A股构建含(名称, 代码, 上市日期, 发行价, 现价, 涨跌幅)六个字段的元组,输出首元素、末元素与索引2~5的切片;再仿照平台任务3,
del掉该元组并重建一个追加”市盈率”字段的七字段新元组后输出。参考答案(点开前请先独立完成)
解题思路:沿用平台任务的“中国通号”六字段数据(名称、代码、上市日期、发行价 5.85、现价 12.27、涨跌幅 1.09744)构建元组;首元素用
tup[0]、末元素用tup[-1]、索引 2~5 的切片含头不含尾写tup[2:6]。第二问先del stock_tup解除变量绑定,再重建含“市盈率”的七字段新元组(市盈率取示例值 33.06,重点在重建写法而非数值本身)。# 变式程序:六字段元组的访问,以及del后重建七字段元组 stock_tup = ('中国通号', '688009', '2019年7月22日', 5.85, 12.27, 1.09744) # 六字段股票信息元组 print(stock_tup[0]) # 输出首元素(名称) print(stock_tup[-1]) # 输出末元素(涨跌幅) print(stock_tup[2:6]) # 输出索引2~5的切片(含头不含尾) del stock_tup # 删除整个变量绑定 stock_tup_new = ('中国通号', '688009', '2019年7月22日', 5.85, 12.27, 1.09744, 33.06) # 重建并追加市盈率字段 print(stock_tup_new) # 输出七字段新元组预期输出(本机 peter 环境实际运行结果,具体以平台运行结果为准):
中国通号 1.09744 ('2019年7月22日', 5.85, 12.27, 1.09744) ('中国通号', '688009', '2019年7月22日', 5.85, 12.27, 1.09744, 33.06)注意:以上为本题变式的独立代码;列表 42.2 与 列表 42.3 对应平台任务的原始代码块仍须原样输入教学平台,不要用本变式替换。
回扣主线章:同型字段打包与重建的完整解析见第 章节 2 章与本章正文。